package com.burgeon.backend.SHOP.service.Impl;

import com.burgeon.backend.SHOP.entity.PayData;
import com.burgeon.backend.SHOP.entity.Wechat;
import com.burgeon.backend.SHOP.mappers.WechatAppMapper;
import com.burgeon.backend.SHOP.service.PayWechatService;
import com.burgeon.backend.SHOP.service.util.MathUtil;
import com.burgeon.backend.SHOP.service.util.WeChatConfigUtil;
import com.burgeon.backend.SHOP.wechat.Util.WXPayUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.*;

@Service
public class PayWechatServiceImpl implements PayWechatService {
    private final static Log logger = LogFactory.getLog(PayWechatService.class);
    @Autowired
    private WechatAppMapper wechatAppMapper;

    @Override
    public HashMap<String,String> goodsRefund(String token, String orderId, String price, String orderPrice,
                               String cause, String refundNo) {
        System.err.println("*************"+price);
        String URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
        SortedMap<String, String> map = new TreeMap<>();
        Wechat wechat = wechatAppMapper.getWechat(token);
        String signType = "MD5";
        String nonce_str = WXPayUtil.generateNonceStr();//随机字符串

        String notify_url = "https://lfwd.cloudsmicro.com/shop/pay/refundResult";

        map.put("appid", wechat.getAppid());//小程序ID
        map.put("mch_id", wechat.getMchId());//商户号
        map.put("nonce_str", nonce_str);//随机字符串
        map.put("sign_type", signType);//签名类型
        map.put("out_trade_no", orderId);//商户订单号
        map.put("out_refund_no", refundNo);//商户退款单号
        map.put("total_fee", orderPrice);//订单金额
        map.put("refund_fee", price);//退款金额
        map.put("refund_desc", cause);//退款原因
        map.put("notify_url", notify_url);//退款结果通知url

        StringBuilder xml = new StringBuilder();

        xml.append("<xml>");

        xml.append("<appid>").append(wechat.getAppid()).append("</appid>");
        xml.append("<mch_id>").append(wechat.getMchId()).append("</mch_id>");
        xml.append("<nonce_str>").append(nonce_str).append("</nonce_str>");
        xml.append("<sign_type>").append(signType).append("</sign_type>");
        xml.append("<out_trade_no>").append(orderId).append("</out_trade_no>");
        xml.append("<out_refund_no>").append(refundNo).append("</out_refund_no>");
        xml.append("<total_fee>").append(orderPrice).append("</total_fee>");
        xml.append("<refund_fee>").append(price).append("</refund_fee>");
        xml.append("<notify_url>").append(notify_url).append("</notify_url>");
        xml.append("<refund_desc>").append(cause).append("</refund_desc>");
        xml.append("<sign>").append(getSign(map, wechat.getPayKey())).append("</sign>");
        xml.append("</xml>");
        System.err.println(xml);
        String row = null;
        try {
            row = WeChatConfigUtil.doRefund(wechat.getMchId(), URL, xml.toString(), token);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.err.println("pay**");
        Map<String, String> data = new HashMap<>();
        try {
            data = WXPayUtil.xmlToMap(row);
        } catch (Exception e) {
            logger.error(e.getMessage());
            e.printStackTrace();
        }
        System.err.println(data);
        String returnCode = data.get("return_code");
        String resultCode = data.get("result_code");
        String errCodeDes = data.get("err_code_des");

        HashMap<String,String> res = new HashMap<String,String>();

        res.put("returnCode", returnCode);
        res.put("resultCode", resultCode);
        res.put("errCodeDes",errCodeDes);

        return res;

    }

    @Override
    public PayData createUnified(String notify_url, String openid, String token, String out_trade_no, String total_fee, String body) throws Exception {
        System.err.println("openid=" + openid + ",token=" + token + ",out_trade_no=" + out_trade_no + ",total_fee=" + total_fee + ",body=" + body);
        String URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        //total_fee = String.valueOf(Long.valueOf(total_fee) * 100);//还原价格
        BigDecimal decimal = MathUtil.multiply(new BigDecimal(total_fee), new BigDecimal(100),0);
        String data = unifiedOrder(notify_url,openid, token, out_trade_no, decimal.toString(), body);
        System.err.println("data =" + data);
        logger.error(data);
        String str = null;
        str = sendPost(URL, data);
        System.err.println("str=" + str);
        Map wdata = WXPayUtil.xmlToMap(str);
        String state = wdata.get("return_code").toString();
        if (!"SUCCESS".equals(state)) {
            logger.error(wdata.toString());
            System.err.println("获取预支付失败" + wdata);
            return null;
        }
        System.err.println("state====="+state);
        System.err.println("pid===="+wdata.get("prepay_id").toString());
        String pid = wdata.get("prepay_id").toString();

        Wechat wechat = wechatAppMapper.getWechat(token);
        SortedMap<String, String> map = new TreeMap<>();
        String appID = wechat.getAppid();
        String nonce_str = WXPayUtil.generateNonceStr();//随机字符串
        String stamp = String.valueOf(WXPayUtil.getCurrentTimestamp());
        String signType = "MD5";
        map.put("appId", appID);
        map.put("timeStamp", stamp);
        map.put("nonceStr", nonce_str);
        map.put("signType", signType);
        map.put("package", "prepay_id=" + pid);
        PayData payData = new PayData();
        payData.setPrepayId("prepay_id=" + pid);
        payData.setNonceStr(nonce_str);
        payData.setPaySign(getSign(map, wechat.getPayKey()));
        payData.setTimeStamp(stamp);

        return payData;
    }

    public String unifiedOrder(String notify_url,String openid, String token, String out_trade_no, String total_fee, String body) {
        if (body.length() > 18) {
            body = body.replace("?", "").substring(0, 18) + "...";
        }
        System.err.println(body);
        body = "PleaseCheckTheOrder";
        Wechat wechat = wechatAppMapper.getWechat(token);
        System.err.println(wechat);
        //第一步
        //公众账号ID  (微信支付分配的公众账号ID)
        String appID = wechat.getAppid();
        //微信支付分配的商户号
        String mch_id = wechat.getMchId();
        //异步接收微信支付结果通知的回调地址，通知url必须为外网可访问的url，不能携带参数。


        //随机字符串，长度要求在32位以内。
        String nonce_str = WXPayUtil.generateNonceStr();
        String ip = "127.0.0.1";

        //第二部：把这些必须的参数获取保存到map集合里
        SortedMap<String, String> map = new TreeMap<>();

        map.put("appid", appID);
        map.put("mch_id", mch_id);
        map.put("nonce_str", nonce_str);
        map.put("body", body);
        map.put("openid", openid);
        map.put("total_fee", total_fee);
        map.put("notify_url", notify_url);
        //商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|* 且在同一个商户号下唯一
        map.put("out_trade_no", out_trade_no);
        //支付的交易类型
        map.put("trade_type", "JSAPI");
        //调用微信支付API的机器IP，也就是商户的机器地址
        map.put("spbill_create_ip", "127.0.0.1");

        //第三步
        // WeChatUtil.getSign(map)：通过这些参数调用逻辑传入参数按照字段名的 ASCII 码从小到大排序（字典序）
        //最后拼接上key得到字符串，并进行MD5运算，再将得到的字符串所有字符转换为大写，得到sign值signValue。(签名)
        //得到sign签名后把这些参数转成xml格式，以下的这些参数是必须的参数，为什么转成xml格式是因为微信那边定义了，我们必须定义xml格式
        StringBuilder xml = new StringBuilder();
        // xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        xml.append("<xml>");
        xml.append("<openid>").append(openid).append("</openid>");
        xml.append("<appid>").append(appID).append("</appid>");
        xml.append("<body>").append(new String(body.getBytes(StandardCharsets.UTF_8))).append("</body>");
        xml.append("<mch_id>").append(mch_id).append("</mch_id>");
        xml.append("<nonce_str>").append(nonce_str).append("</nonce_str>");
        xml.append("<notify_url>").append(notify_url).append("</notify_url>");
        xml.append("<out_trade_no>").append(out_trade_no).append("</out_trade_no>");
        xml.append("<spbill_create_ip>").append(ip).append("</spbill_create_ip>");
        xml.append("<total_fee>").append(total_fee).append("</total_fee>");
        xml.append("<trade_type>JSAPI</trade_type>");
        xml.append("<sign>").append(getSign(map, wechat.getPayKey())).append("</sign>");
        xml.append("</xml>");

        //第四步 远程调用微信支付接口，接受返回数据
        //return unifiedOrder(xml.toString());
        return xml.toString();
    }

    //发送
    private static String sendPost(String url, String param) {
        PrintWriter out = null;
        BufferedReader in = null;
        StringBuilder result = new StringBuilder();
        try {
            String urlNameString = url + "?" + param;
            URL realUrl = new URL(urlNameString);
            URLConnection conn = realUrl.openConnection();
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Accept-Charset", "UTF-8");
            conn.setRequestProperty("contentType", "UTF-8");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            out = new PrintWriter(conn.getOutputStream());
            out.print(param);
            out.flush();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    /**
     * 生成签名
     *
     * @param map
     * @return
     */
    private static String getSign(SortedMap<String, String> map, String payKey) {
        StringBuffer sb = new StringBuffer();
        Set<?> es = map.entrySet();
        Iterator<?> it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if ((null != v) && (!"".equals(v)) && (!"sign".equals(k))
                    && (!"key".equals(k))) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + payKey);

        return MD5(sb.toString()).toUpperCase();
    }

    private static String MD5(String s) {
        char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        try {
            byte[] btInput = s.getBytes(StandardCharsets.UTF_8);
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            byte[] md = mdInst.digest(btInput);
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
